home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue60 / Alfresco / AAHuffmn.pas < prev    next >
Encoding:
Pascal/Delphi Source File  |  2000-06-25  |  29.1 KB  |  946 lines

  1. {*********************************************************}
  2. {* AAHuffmn                                              *}
  3. {* Copyright (c) Julian M Bucknall 1999, 2000            *}
  4. {* All rights reserved.                                  *}
  5. {*********************************************************}
  6. {* Huffman compression and decompression                 *}
  7. {*********************************************************}
  8.  
  9. {Note: this unit is released as freeware. In other words, you are free
  10.        to use this unit in your own applications, however I retain all
  11.        copyright to the code. JMB}
  12.  
  13. unit AAHuffmn;
  14.  
  15. {Version 1: initial release}
  16. {Version 2: New method for writing/reading the Huffman tree}
  17.  
  18. interface
  19.  
  20. uses
  21.   SysUtils, Classes;
  22.  
  23. {$IFOPT D+}
  24. {$DEFINE InDebugMode}
  25. {$ENDIF}
  26.  
  27. procedure HuffmanCompress(aInStream, aOutStream : TStream);
  28. procedure HuffmanDecompress(aInStream, aOutStream : TStream);
  29.  
  30. implementation
  31.  
  32. const
  33.   vaByte    = 0;   {value is a byte: 0..255}
  34.   vaWord    = 1;   {value is a word: 255..65535}
  35.   vaLongint = 2;   {value is a longint: all other values}
  36.  
  37. const
  38.   Bit : array [0..7] of byte =  {bit masks}
  39.         ($01, $02, $04, $08, $10, $20, $40, $80);
  40.  
  41. type
  42.   PHuffmanNode = ^THuffmanNode;
  43.   THuffmanNode = packed record
  44.     hnCount    : longint;
  45.     hnLeftInx  : longint;
  46.     hnRightInx : longint;
  47.   end;
  48.  
  49.   PHuffmanTree = ^THuffmanTree;
  50.   THuffmanTree = array [0..510] of THuffmanNode;
  51.  
  52. type
  53.   THuffmanCodeStr = string[255];
  54.  
  55.   PHuffmanCode = ^THuffmanCode;
  56.   THuffmanCode = packed record
  57.     hcBitCount : longint;
  58.     hcCode     : array [0..31] of byte;
  59.   end;
  60.  
  61.   PHuffmanCodes = ^THuffmanCodes;
  62.   THuffmanCodes = array [0..255] of THuffmanCode;
  63.  
  64.  
  65. {===THuffmanPriorityQueue=============================================}
  66. type
  67.   longint = integer;
  68.  
  69.   THuffmanPriorityQueue = class
  70.     {-A priority queue for Huffman compression}
  71.     private
  72.       pqList : TList;
  73.       pqTree : PHuffmanTree;
  74.     protected
  75.       function pqGetCount : integer;
  76.  
  77.       procedure pqBubbleUp(aFromInx : integer; aItem : longint);
  78.       procedure pqTrickleDown(aFromInx : integer; aItem : longint);
  79.     public
  80.       constructor Create(aHTree : PHuffmanTree);
  81.         {-Create the priority queue}
  82.       destructor Destroy; override;
  83.         {-Dispose of the priority queue}
  84.  
  85.       procedure Add(aItem : longint);
  86.         {-Add an item (ie, Huffman tree index) to the priority queue}
  87.       function Remove : longint;
  88.         {-Remove and return the item (ie, Huffman tree index) with the
  89.           smallest count}
  90.  
  91.       property Count : integer read pqGetCount;
  92.         {-Count of items in the queue}
  93.  
  94.       property List : TList read pqList;
  95.   end;
  96. {--------}
  97. constructor THuffmanPriorityQueue.Create(aHTree : PHuffmanTree);
  98. begin
  99.   inherited Create;
  100.   {create the queue's array; we know it'll be at most 256 elements}
  101.   pqList := TList.Create;
  102.   pqList.Capacity := 256;
  103.   {remember the Huffman tree we're using}
  104.   pqTree := aHTree;
  105. end;
  106. {--------}
  107. destructor THuffmanPriorityQueue.Destroy;
  108. begin
  109.   pqList.Free;
  110.   inherited Destroy;
  111. end;
  112. {--------}
  113. procedure THuffmanPriorityQueue.Add(aItem : longint);
  114. begin
  115.   {add extra space at the end of the queue}
  116.   pqList.Count := pqList.Count + 1;
  117.   {now bubble the item up as far as it will go}
  118.   pqBubbleUp(pred(pqList.Count), aItem);
  119. end;
  120. {--------}
  121. procedure THuffmanPriorityQueue.pqBubbleUp(aFromInx : integer;
  122.                                            aItem    : longint);
  123. var
  124.   ParentInx : integer;
  125.   ItemCount : longint;
  126. begin
  127.   {while the item under consideration is smaller than its parent, swap
  128.    it with its parent and continue from its new position}
  129.   {Note: the parent for the child at index N is at (N-1) div 2}
  130.   ItemCount := pqTree^[aItem].hnCount;
  131.   ParentInx := (aFromInx - 1) div 2;
  132.   {while our item has a parent, and it's greater than the parent...}
  133.   while (aFromInx > 0) and
  134.         (ItemCount <
  135.            pqTree^[longint(pqList[ParentInx])].hnCount) do begin
  136.     {move our parent down the tree}
  137.     pqList[aFromInx] := pqList[ParentInx];
  138.     aFromInx := ParentInx;
  139.     ParentInx := (aFromInx - 1) div 2;
  140.   end;
  141.   {store our item in the correct place}
  142.   pqList[aFromInx] := pointer(aItem);
  143. end;
  144. {--------}
  145. function THuffmanPriorityQueue.pqGetCount : integer;
  146. begin
  147.   Result := pqList.Count;
  148. end;
  149. {--------}
  150. procedure THuffmanPriorityQueue.pqTrickleDown(aFromInx : integer;
  151.                                               aItem    : longint);
  152. var
  153.   ChildInx  : integer;
  154.   ListCount : integer;
  155.   ItemCount : longint;
  156. begin
  157.   {while the item under consideration is greater than one of its
  158.    children, swap it with the smaller child and continue from its new
  159.    position}
  160.   {Note: the children for the parent at index N are at (2N+1) and
  161.          2N+2}
  162.   ItemCount := pqTree^[aItem].hnCount;
  163.   ListCount := pqList.Count;
  164.   {calculate the left child index}
  165.   ChildInx := succ(aFromInx * 2);
  166.   {while there is at least a left child...}
  167.   while (ChildInx < ListCount) do begin
  168.     {if there is a right child, calculate the index of the smaller
  169.      child}
  170.     if (succ(ChildInx) < ListCount) and
  171.        (pqTree^[longint(pqList[ChildInx])].hnCount >
  172.           pqTree^[longint(pqList[succ(ChildInx)])].hnCount) then
  173.       inc(ChildInx);
  174.     {if our item is less or equal to the smaller child, we're done}
  175.     if (ItemCount <= pqTree^[longint(pqList[ChildInx])].hnCount) then
  176.       Break;
  177.     {otherwise move the smaller child up the tree, and move our item
  178.      down the tree and repeat}
  179.     pqList[aFromInx] := pqList[ChildInx];
  180.     aFromInx := ChildInx;
  181.     ChildInx := succ(aFromInx * 2);
  182.   end;
  183.   {store our item in the correct place}
  184.   pqList[aFromInx] := pointer(aItem);
  185. end;
  186. {--------}
  187. function THuffmanPriorityQueue.Remove : longint;
  188. begin
  189.   {return the item at the root}
  190.   Result := longint(pqList[0]);
  191.   {replace the root with the child at the lowest, rightmost position,
  192.    and shrink the list}
  193.   pqList[0] := pqList.Last;
  194.   pqList.Count := pqList.Count - 1;
  195.   {now trickle down the root item as far as it will go}
  196.   if (pqList.Count > 0) then
  197.     pqTrickleDown(0, longint(pqList[0]));
  198. end;
  199. {====================================================================}
  200.  
  201.  
  202. {===bit streams======================================================}
  203. const
  204.   StreamBufferSize = 4096;
  205. type
  206.   TInputBitStream = class
  207.     private
  208.       FAccum      : byte;
  209.       FBufEnd     : integer;
  210.       FBuffer     : PAnsiChar;
  211.       FBufPos     : integer;
  212.       FMask       : byte;
  213.       FStream     : TStream;
  214.     protected
  215.       procedure ibsReadBuffer;
  216.     public
  217.       constructor Create(aStream : TStream);
  218.       destructor Destroy; override;
  219.  
  220.       function ReadBit : boolean;
  221.       function ReadByte : byte;
  222.   end;
  223.   TOutputBitStream = class
  224.     private
  225.       FAccum      : byte;
  226.       FBuffer     : PAnsiChar;
  227.       FBufPos     : integer;
  228.       FMask       : byte;
  229.       FStream     : TStream;
  230.       FStrmBroken : boolean;
  231.     protected
  232.       procedure obsWriteBuffer;
  233.     public
  234.       constructor Create(aStream : TStream);
  235.       destructor Destroy; override;
  236.  
  237.       procedure WriteBit(aBit : boolean);
  238.       procedure WriteByte(aByte : byte);
  239.   end;
  240. {--------}
  241. constructor TInputBitStream.Create(aStream : TStream);
  242. begin
  243.   inherited Create;
  244.   FStream := aStream;
  245.   GetMem(FBuffer, StreamBufferSize);
  246. end;
  247. {--------}
  248. destructor TInputBitStream.Destroy;
  249. begin
  250.   if (FBuffer <> nil) then
  251.     FreeMem(FBuffer, StreamBufferSize);
  252.   inherited Destroy;
  253. end;
  254. {--------}
  255. procedure TInputBitStream.ibsReadBuffer;
  256. begin
  257.   FBufEnd := FStream.Read(FBuffer^, StreamBufferSize);
  258.   if (FBufEnd = 0) then
  259.     raise Exception.Create('No more data in input stream');
  260.   FBufPos := 0;
  261. end;
  262. {--------}
  263. function TInputBitStream.ReadBit : boolean;
  264. begin
  265.   {if we have no bits left in the current accumulator, read another
  266.    accumulator byte and reset the mask}
  267.   if (FMask = 0) then begin
  268.     if (FBufPos >= FBufEnd) then
  269.       ibsReadBuffer;
  270.     FAccum := byte(FBuffer[FBufPos]);
  271.     inc(FBufPos);
  272.     FMask := 1;
  273.   end;
  274.   {take the next bit}
  275.   Result := (FAccum and FMask) <> 0;
  276.   FMask := FMask shl 1;          {overflow required on this statement}
  277. end;
  278. {--------}
  279. function TInputBitStream.ReadByte : byte;
  280. var
  281.   Mask   : byte;
  282.   Accum  : byte;
  283.   ByteMask : byte;
  284. begin
  285.   {to speed up this process, we shall take copies of the object's
  286.    fields; at the end we'll copy them back}
  287.   Mask := FMask;
  288.   Accum := FAccum;
  289.   {prepare for the loop(s)}
  290.   ByteMask := 1;
  291.   Result := 0;
  292.   {extract as many bits from the accumulator as we can, refilling as
  293.    necessary}
  294.   while (ByteMask <> 0) do begin
  295.     {if the accumulator is empty, refill it and reset the mask}
  296.     if (Mask = 0) then begin
  297.       if (FBufPos >= FBufEnd) then
  298.         ibsReadBuffer;
  299.       Accum := byte(FBuffer[FBufPos]);
  300.       inc(FBufPos);
  301.       Mask := 1;
  302.     end;
  303.     {get the next bit}
  304.     if ((Accum and Mask) <> 0) then
  305.       Result := Result or ByteMask;
  306.     Mask := Mask shl 1;          {overflow required on this statement}
  307.     ByteMask := ByteMask shl 1;  {overflow required on this statement}
  308.   end;
  309.   {save the new values of the accumulator and the mask}
  310.   FMask := Mask;
  311.   FAccum := Accum;
  312. end;
  313. {--------}
  314. constructor TOutputBitStream.Create(aStream : TStream);
  315. begin
  316.   inherited Create;
  317.   FStream := aStream;
  318.   GetMem(FBuffer, StreamBufferSize);
  319.   FMask := 1; {ready for the first bit to be written}
  320. end;
  321. {--------}
  322. destructor TOutputBitStream.Destroy;
  323. begin
  324.   if (FBuffer <> nil) then begin
  325.     {if Mask is not equal to 1, it means that there are some bits in
  326.      the accumulator that need to be written to the buffer; make sure
  327.      the buffer is written to the underlying stream}
  328.     if not FStrmBroken then begin
  329.       if (FMask <> 1) then begin
  330.         byte(FBuffer[FBufPos]) := FAccum;
  331.         inc(FBufPos);
  332.       end;
  333.       if (FBufPos > 0) then
  334.         obsWriteBuffer;
  335.     end;
  336.     FreeMem(FBuffer, StreamBufferSize);
  337.   end;
  338.   inherited Destroy;
  339. end;
  340. {--------}
  341. procedure TOutputBitStream.obsWriteBuffer;
  342. var
  343.   BytesWrit : longint;
  344. begin
  345.   BytesWrit := FStream.Write(FBuffer^, FBufPos);
  346.   if (BytesWrit <> FBufPos) then begin
  347.     {we had a problem writing the buffer to the stream; raiuse an
  348.      exception to say so, but first make sure so that we don't trigger
  349.      the same exception in the Destroy as well}
  350.     FStrmBroken := true;
  351.     raise Exception.Create('Failed to write buffer to output stream');
  352.   end;
  353.   FBufPos := 0;
  354. end;
  355. {--------}
  356. procedure TOutputBitStream.WriteBit(aBit : boolean);
  357. begin
  358.   {set the next spare bit}
  359.   if aBit then
  360.     FAccum := (FAccum or FMask);
  361.   FMask := FMask shl 1;           {require overflow on this statement}
  362.   {if we have no spare bits left in the current accumulator, write it
  363.    to the buffer, and reset the accumulator and the mask}
  364.   if (FMask = 0) then begin
  365.     byte(FBuffer[FBufPos]) := FAccum;
  366.     inc(FBufPos);
  367.     if (FBufPos >= StreamBufferSize) then
  368.       obsWriteBuffer;
  369.     FAccum := 0;
  370.     FMask := 1;
  371.   end;
  372. end;
  373. {--------}
  374. procedure TOutputBitStream.WriteByte(aByte : byte);
  375. var
  376.   Mask   : byte;
  377.   Accum  : byte;
  378.   ByteMask : byte;
  379. begin
  380.   {to speed up this process, we shall take copies of the object's
  381.    fields; at the end we'll copy them back}
  382.   Mask := FMask;
  383.   Accum := FAccum;
  384.   {prepare for the loop}
  385.   ByteMask := 1;
  386.   {store as many bits to the accumulator as we can, writing it out and
  387.    clearing it as necessary}
  388.   while (ByteMask <> 0) do begin
  389.     {store the next bit}
  390.     if ((aByte and ByteMask) <> 0) then
  391.       Accum := Accum or Mask;
  392.     Mask := Mask shl 1;          {overflow required on this statement}
  393.     ByteMask := ByteMask shl 1;  {overflow required on this statement}
  394.     {if needed, write out the accumulator & reset}
  395.     if (Mask = 0) then begin
  396.       byte(FBuffer[FBufPos]) := Accum;
  397.       inc(FBufPos);
  398.       if (FBufPos >= StreamBufferSize) then
  399.         obsWriteBuffer;
  400.       Accum := 0;
  401.       Mask := 1;
  402.     end;
  403.   end;
  404.   {save the new values of the accumulator and the mask}
  405.   FMask := Mask;
  406.   FAccum := Accum;
  407. end;
  408. {====================================================================}
  409.  
  410.  
  411. {===Exception handling===============================================}
  412. procedure RaiseWriteError;
  413. begin
  414.   raise Exception.Create('Cannot write to Huffman compressed stream');
  415. end;
  416. {--------}
  417. procedure RaiseReadError;
  418. begin
  419.   raise Exception.Create('Expecting more data in Huffman compressed stream, but none left');
  420. end;
  421. {--------}
  422. procedure RaiseReadCorruptError;
  423. begin
  424.   raise Exception.Create('Huffman compressed stream contains corrupted data');
  425. end;
  426. {====================================================================}
  427.  
  428.  
  429. {===Helper routines==================================================}
  430. procedure WriteBits(const aHCode  : THuffmanCode;
  431.                           aStream : TOutputBitStream);
  432. var
  433.   ByteNum : integer;
  434.   BitNum  : integer;
  435.   i       : integer;
  436. begin
  437.   {start off with the correct mask}
  438.   ByteNum := 0;
  439.   BitNum := 7;
  440.   {for all bits...}
  441.   for i := 0 to pred(aHCode.hcBitCount) do begin
  442.     {write the current bit}
  443.     aStream.WriteBit((aHCode.hcCode[ByteNum] and Bit[BitNum]) <> 0);
  444.     {get next bit}
  445.     if (BitNum = 0) then begin
  446.       BitNum := 7;
  447.       inc(ByteNum);
  448.     end
  449.     else
  450.       dec(BitNum);
  451.   end;
  452. end;
  453. {--------}
  454. function ReadChar(aStream : TStream) : char;
  455. {-read a character from the stream}
  456. var
  457.   BytesRead : integer;
  458. begin
  459.   BytesRead := aStream.Read(Result, sizeof(char));
  460.   if (BytesRead <> sizeof(char)) then
  461.     RaiseReadError;
  462. end;
  463. {--------}
  464. function ReadValue(aStream : TStream) : longint;
  465. {-read an integer value from the stream}
  466. var
  467.   BytesRead : integer;
  468.   ValueType : byte;
  469. begin
  470.   Result := 0;
  471.   BytesRead := aStream.Read(ValueType, sizeof(ValueType));
  472.   if (BytesRead <> sizeof(ValueType)) then
  473.     RaiseReadError;
  474.   case ValueType of
  475.     vaByte :
  476.       begin
  477.         BytesRead := aStream.Read(Result, sizeof(byte));
  478.         if (BytesRead <> sizeof(byte)) then
  479.           RaiseReadError;
  480.       end;
  481.     vaWord :
  482.       begin
  483.         BytesRead := aStream.Read(Result, sizeof(word));
  484.         if (BytesRead <> sizeof(word)) then
  485.           RaiseReadError;
  486.       end;
  487.     vaLongint :
  488.       begin
  489.         BytesRead := aStream.Read(Result, sizeof(longint));
  490.         if (BytesRead <> sizeof(longint)) then
  491.           RaiseReadError;
  492.       end;
  493.   else {it's an unknown value type}
  494.     RaiseReadCorruptError;
  495.   end;{case}
  496. end;
  497. {--------}
  498. procedure WriteChar(aStream : TStream; aChar : char);
  499. {-write a character to the stream}
  500. var
  501.   BytesWrit : integer;
  502. begin
  503.   BytesWrit := aStream.Write(aChar, sizeof(char));
  504.   if (BytesWrit <> sizeof(char)) then
  505.     RaiseWriteError;
  506. end;
  507. {--------}
  508. procedure WriteValue(aStream : TStream; aValue : longint);
  509. {-write an integer value to the stream}
  510. var
  511.   BytesWrit : integer;
  512.   ValueType : byte;
  513. begin
  514.   {if the value is between 0 and 255 write a byte to the stream}
  515.   if (0 <= aValue) and (aValue < 256) then begin
  516.     ValueType := vaByte;
  517.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  518.     if (BytesWrit <> sizeof(ValueType)) then
  519.       RaiseWriteError;
  520.     BytesWrit := aStream.Write(aValue, sizeof(byte));
  521.     if (BytesWrit <> sizeof(byte)) then
  522.       RaiseWriteError;
  523.   end
  524.   {if the value is between 256 and 65535 write a word to the stream}
  525.   else if (256 <= aValue) and (aValue < 64*1024) then begin
  526.     ValueType := vaWord;
  527.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  528.     if (BytesWrit <> sizeof(ValueType)) then
  529.       RaiseWriteError;
  530.     BytesWrit := aStream.Write(aValue, sizeof(word));
  531.     if (BytesWrit <> sizeof(word)) then
  532.       RaiseWriteError;
  533.   end
  534.   {otherwise write a longint to the stream}
  535.   else begin
  536.     ValueType := vaLongint;
  537.     BytesWrit := aStream.Write(ValueType, sizeof(ValueType));
  538.     if (BytesWrit <> sizeof(ValueType)) then
  539.       RaiseWriteError;
  540.     BytesWrit := aStream.Write(aValue, sizeof(longint));
  541.     if (BytesWrit <> sizeof(longint)) then
  542.       RaiseWriteError;
  543.   end;
  544. end;
  545. {--------}
  546. procedure CalcCharDistribution(aStream : TStream;
  547.                                aHTree  : PHuffmanTree);
  548. {-calculate the character distribution from the data in the stream;
  549.   fill the first 256 entries in the Huffman tree with the information}
  550. var
  551.   i         : integer;
  552.   Buffer    : PByteArray;
  553.   BytesRead : integer;
  554. begin
  555.   aStream.Position := 0;
  556.   GetMem(Buffer, 1024);
  557.   try
  558.     BytesRead := aStream.Read(Buffer^, 1024);
  559.     while (BytesRead <> 0) do begin
  560.       for i := pred(BytesRead) downto 0 do
  561.         inc(aHTree^[Buffer^[i]].hnCount);
  562.       BytesRead := aStream.Read(Buffer^, 1024);
  563.     end;
  564.   finally
  565.     FreeMem(Buffer, 1024);
  566.   end;
  567. end;
  568. {--------}
  569. procedure ConvertCodeStr(const aHCode  : THuffmanCodeStr;
  570.                                aHCodes : PHuffmanCodes;
  571.                                aNodeInx: integer);
  572. {-convert a code string into binary; store in codes array}
  573. var
  574.   TempCode : THuffmanCode;
  575.   ByteNum  : integer;
  576.   BitNum   : byte;
  577.   i        : integer;
  578. begin
  579.   {set the binary code to zeros, so we only have to record '1' bits}
  580.   FillChar(TempCode, sizeof(TempCode), 0);
  581.   {store the code length}
  582.   TempCode.hcBitCount := length(aHCode);
  583.   {fill the bits from the left in the binary code}
  584.   ByteNum := 0;
  585.   BitNum := 7;
  586.   for i := 1 to length(aHCode) do begin
  587.     if (aHCode[i] = '1') then
  588.       TempCode.hcCode[ByteNum] :=
  589.          TempCode.hcCode[ByteNum] or Bit[BitNum];
  590.     if (BitNum = 0) then begin
  591.       BitNum := 7;
  592.       inc(ByteNum);
  593.     end
  594.     else
  595.       dec(BitNum);
  596.   end;
  597.   {store binary code in the codes array}
  598.   aHCodes^[aNodeInx] := TempCode;
  599. end;
  600. {--------}
  601. procedure CalcHuffmanCodePrim(aNodeInx : integer;
  602.                           var aHCode   : THuffmanCodeStr;
  603.                               aHTree   : PHuffmanTree;
  604.                               aHCodes  : PHuffmanCodes);
  605. {-recursive routine to calculate all the Huffman codes for a given
  606.   Huffman tree}
  607. begin
  608.   {if the current node is not a leaf, then visit the left subtree
  609.    followed by the right subtree}
  610.   if (aNodeInx >= 256) then begin
  611.     {add a 0 bit on the end of the code string}
  612.     inc(aHCode[0]);
  613.     aHCode[length(aHCode)] := '0';
  614.     {visit the left subtree}
  615.     CalcHuffmanCodePrim(aHTree^[aNodeInx].hnLeftInx, aHCode, aHTree, aHCodes);
  616.     {add a 1 bit on the end of the code string}
  617.     aHCode[length(aHCode)] := '1';
  618.     {visit the right subtree}
  619.     CalcHuffmanCodePrim(aHTree^[aNodeInx].hnRightInx, aHCode, aHTree, aHCodes);
  620.     dec(aHCode[0]);
  621.   end
  622.   {if the current node is a leaf, record the current code in the codes
  623.    array}
  624.   else begin
  625.     ConvertCodeStr(aHCode, aHCodes, aNodeInx);
  626.   end;
  627. end;
  628. {--------}
  629. procedure CalcHuffmanCodes(aHTree  : PHuffmanTree;
  630.                            aRoot   : integer;
  631.                            aHCodes : PHuffmanCodes);
  632. {-calculate the Huffman codes for a Huffman tree}
  633. var
  634.   HCode : THuffmanCodeStr;
  635. begin
  636.   {clear the codes array}
  637.   FillChar(aHCodes^, sizeof(aHCodes^), 0);
  638.   {to calculate the codes we have to visit every leaf and for each
  639.    leaf we'll have accumulated a series of bits (going left from a
  640.    parent node to a child node is a 0 bit, going right is a 1 bit);
  641.    for the walk through the tree we'll use a modified inorder
  642.    traversal (ie, visit the left subtree, there's no need to visit the
  643.    node itself, visit the right subtree); because we know the tree has
  644.    a maximum depth of 255, we'll use recursion without getting too
  645.    worried about blowing the stack}
  646.   HCode := '';
  647.   CalcHuffmanCodePrim(aRoot, HCode, aHTree, aHCodes);
  648. end;
  649. {--------}
  650. function ReadNode(aStream : TInputBitStream;
  651.                   aHTree  : PHuffmanTree;
  652.               var aMaxInx : integer) : integer;
  653. var
  654.   IsLeaf  : boolean;
  655. begin
  656.   {read the next bit to determine which node we have to create}
  657.   IsLeaf := aStream.ReadBit;
  658.   {if it's a leaf then return its node index (ie, the character)}
  659.   if IsLeaf then
  660.     Result := aStream.ReadByte
  661.   {if it's an internal node, get the left and right subtrees}
  662.   else begin
  663.     inc(aMaxInx);
  664.     Result := aMaxInx;
  665.     aHTree^[Result].hnLeftInx := ReadNode(aStream, aHTree, aMaxInx);
  666.     aHTree^[Result].hnRightInx := ReadNode(aStream, aHTree, aMaxInx);
  667.   end;
  668. end;
  669. {--------}
  670. function ReadCharDistribution(aStream : TInputBitStream;
  671.                               aHTree  : PHuffmanTree) : integer;
  672. {-read a character distribution from a stream}
  673. var
  674.   MaxInx : integer;
  675. begin
  676.   MaxInx := 255;
  677.   Result := ReadNode(aStream, aHTree, MaxInx);
  678. end;
  679. {--------}
  680. procedure WriteNode(aStream  : TOutputBitStream;
  681.                     aHTree   : PHuffmanTree;
  682.                     aNodeInx : integer);
  683. begin
  684.   {for a leaf, write a 1 bit, followed by the character}
  685.   if (aNodeInx < 256) then begin
  686.     aStream.WriteBit(true);
  687.     aStream.WriteByte(aNodeInx);
  688.   end
  689.   {for an internal node, write a 0 bit, then the left subtree, then
  690.    the right subtree}
  691.   else begin
  692.     aStream.WriteBit(false);
  693.     WriteNode(aStream, aHTree, aHTree^[aNodeInx].hnLeftInx);
  694.     WriteNode(aStream, aHTree, aHTree^[aNodeInx].hnRightInx);
  695.   end;
  696. end;
  697. {--------}
  698. procedure WriteCharDistribution(aStream : TOutputBitStream;
  699.                                 aHTree  : PHuffmanTree;
  700.                                 aRootInx: integer);
  701. {-write a character distribution to a stream}
  702. begin
  703.   WriteNode(aStream, aHTree, aRootInx);
  704. end;
  705. {--------}
  706. procedure BuildHuffmanTree(aHTree         : PHuffmanTree;
  707.                        var aLastParentInx : integer);
  708. {-given a Huffman tree just containing the character distributions,
  709.   build the entire tree; return the index of the root}
  710. var
  711.   i  : integer;
  712.   PQ : THuffmanPriorityQueue;
  713.   Node1Inx  : longint;
  714.   Node2Inx  : longint;
  715.   ParentInx : integer;
  716. begin
  717.   ParentInx := aLastParentInx;
  718.   {create a priority queue}
  719.   PQ := THuffmanPriorityQueue.Create(aHTree);
  720.   try
  721.     {add all the non-zero nodes to the queue}
  722.     for i := 0 to 255 do
  723.       if (aHTree^[i].hnCount <> 0) then
  724.         PQ.Add(i);
  725.     {SPECIAL CASE: there is only one non-zero node, ie the input
  726.      stream consisted of just one character, repeated one or more
  727.      times; set the parent index to the single character}
  728.     if (PQ.Count = 1) then
  729.       ParentInx := PQ.Remove
  730.     {otherwise we have the normal, many different chars, case}
  731.     else
  732.       {while there is more than one item in the queue, remove the two
  733.        smallest, join them to a new parent, and add the parent to the
  734.        queue}
  735.       while (PQ.Count > 1) do begin
  736.         Node1Inx := PQ.Remove;
  737.         Node2Inx := PQ.Remove;
  738.         inc(ParentInx);
  739.         with aHTree^[ParentInx] do begin
  740.           hnLeftInx := Node1Inx;
  741.           hnRightInx := Node2Inx;
  742.           hnCount := aHTree^[Node1Inx].hnCount +
  743.                      aHTree^[Node2Inx].hnCount;
  744.         end;
  745.         PQ.Add(ParentInx);
  746.       end;
  747.   finally
  748.     PQ.Free;
  749.   end;
  750.   aLastParentInx := ParentInx;
  751. end;
  752. {--------}
  753. procedure DoHuffmanCompression(aInStream  : TStream;
  754.                                aOutStream : TOutputBitStream;
  755.                                aHCodes    : PHuffmanCodes);
  756. {-given an array of Huffman codes, compress the input stream to the
  757.   output stream}
  758. var
  759.   B : byte;
  760.   i : integer;
  761. begin
  762.   {reset the input stream to the start}
  763.   aInStream.Position := 0;
  764.   {for each character in the input stream, write its Huffman code to
  765.    the output stream}
  766.   for i := 0 to pred(aInStream.Size) do begin
  767.     aInStream.Read(B, sizeof(B));
  768.     WriteBits(aHCodes^[B], aOutStream);
  769.   end;
  770. end;
  771. {--------}
  772. procedure DoHuffmanDecompression(aInStream  : TInputBitStream;
  773.                                  aOutStream : TStream;
  774.                                  aHTree     : PHuffmanTree;
  775.                                  aRoot      : integer);
  776. {-given a Huffman tree, decompress the input stream to the output
  777.   stream}
  778. var
  779.   CharCount      : longint;
  780.   TotalCharCount : longint;
  781.   CurrNode       : integer;
  782.   GoLeft         : boolean;
  783.   Ch             : char;
  784. begin
  785.   {calculate the total number of characters to decompress; preset the
  786.    loop variables}
  787.   TotalCharCount := aHTree^[aRoot].hnCount;
  788.   CharCount := 0;
  789.   CurrNode := aRoot;
  790.   {repeat until all the characters have been decompressed}
  791.   while CharCount < TotalCharCount do begin
  792.     {read the next bit}
  793.     GoLeft := not aInStream.ReadBit;
  794.     {walk down the Huffman tree}
  795.     if GoLeft then
  796.       CurrNode := aHTree^[CurrNode].hnLeftInx
  797.     else
  798.       CurrNode := aHTree^[CurrNode].hnRightInx;
  799.     {if we have reached a leaf, output the character concerned, and
  800.      reset the current node to the root}
  801.     if (CurrNode < 256) then begin
  802.       Ch := char(CurrNode);
  803.       aOutStream.Write(Ch, sizeof(byte));
  804.       CurrNode := aRoot;
  805.       inc(CharCount);
  806.     end;
  807.   end;
  808. end;
  809. {--------}
  810. procedure WriteMultipleChars(aStream : TStream;
  811.                              aCh     : char;
  812.                              aCount  : longint);
  813. {-write several copies of a character to a stream}
  814. const
  815.   BufferSize = 1024;
  816. var
  817.   Buffer       : PByteArray;
  818.   BytesToWrite : integer;
  819.   BytesWrit    : integer;
  820. begin
  821.   GetMem(Buffer, BufferSize);
  822.   try
  823.     FillChar(Buffer^, BufferSize, aCh);
  824.     while (aCount > 0) do begin
  825.       if (aCount < BufferSize) then
  826.         BytesToWrite := aCount
  827.       else
  828.         BytesToWrite := BufferSize;
  829.       BytesWrit := aStream.Write(Buffer^, BytesToWrite);
  830.       dec(aCount, BytesWrit);
  831.     end;
  832.   finally
  833.     FreeMem(Buffer, BufferSize);
  834.   end;
  835. end;
  836. {====================================================================}
  837.  
  838.  
  839. {===Interfaced routines==============================================}
  840. procedure HuffmanCompress(aInStream, aOutStream : TStream);
  841. var
  842.   HTree  : PHuffmanTree;
  843.   Root   : integer;
  844.   HCodes : PHuffmanCodes;
  845.   Size   : longint;
  846.   OutputBitStream : TOutputBitStream;
  847. begin
  848.   {write the number of characters in the input stream to the output
  849.    stream; this aids in decompression--we know when to stop}
  850.   Size := aInStream.Size;
  851.   aOutStream.Write(Size, sizeof(Size));
  852.   {if there's nothing to compress, exit now}
  853.   if (Size = 0) then
  854.     Exit;
  855.   {prepare}
  856.   HTree := nil;
  857.   OutputBitStream := nil;
  858.   try
  859.     {allocate the Huffman tree}
  860.     New(HTree);
  861.     {initialise the tree}
  862.     FillChar(HTree^, sizeof(HTree^), 0);
  863.     {get the distribution of characters in the input stream, place in
  864.      the first 256 elements of the Huffman tree}
  865.     CalcCharDistribution(aInStream, HTree);
  866.     {build the Huffman tree}
  867.     Root := 255;
  868.     BuildHuffmanTree(HTree, Root);
  869.     {create the output bit stream}
  870.     OutputBitStream := TOutputBitStream.Create(aOutStream);
  871.     {when this point is reached we know the Huffman tree is rooted at
  872.      Root; if Root is a leaf, then the input stream just consisted of
  873.      repetitions of one character, so output the minimal compressed
  874.      data, essentially RLE compression}
  875.     if (Root < 256) then
  876.       WriteCharDistribution(OutputBitStream, HTree, Root)
  877.     else {Root is not a leaf} begin
  878.       {allocate the codes array}
  879.       New(HCodes);
  880.       try
  881.         {calculate all the codes}
  882.         CalcHuffmanCodes(HTree, Root, HCodes);
  883.         {we are now ready to compress the input stream, however we
  884.          must first output the tree to the output stream to aid the
  885.          decompressor}
  886.         WriteCharDistribution(OutputBitStream, HTree, Root);
  887.         {compress the characters in the input stream}
  888.         DoHuffmanCompression(aInStream, OutputBitStream, HCodes);
  889.       finally
  890.         Dispose(HCodes);
  891.       end;
  892.     end;
  893.   finally
  894.     if (HTree <> nil) then
  895.       Dispose(HTree);
  896.     OutputBitStream.Free;
  897.   end;
  898. end;
  899. {--------}
  900. procedure HuffmanDecompress(aInStream, aOutStream : TStream);
  901. var
  902.   HTree : PHuffmanTree;
  903.   Root  : integer;
  904.   Size  : longint;
  905.   InputBitStream : TInputBitStream;
  906. begin
  907.   {if there's nothing to decompress, exit now}
  908.   if (aInStream.Size = 0) then
  909.     Exit;
  910.   aInStream.ReadBuffer(Size, sizeof(Size));
  911.   if (Size = 0) then
  912.     Exit;
  913.   {prepare}
  914.   HTree := nil;
  915.   InputBitStream := nil;
  916.   try
  917.     {allocate the Huffman tree}
  918.     New(HTree);
  919.     {initialise the tree}
  920.     FillChar(HTree^, sizeof(HTree^), 0);
  921.     {create the input bit stream}
  922.     InputBitStream := TInputBitStream.Create(aInStream);
  923.     {read the Huffman tree from the input stream}
  924.     Root := ReadCharDistribution(InputBitStream, HTree);
  925.     {when this point is reached we know the Huffman tree is rooted at
  926.      Root; if Root is a leaf, then the original stream just consisted
  927.      of repetitions of one character}
  928.     if (Root < 256) then
  929.       WriteMultipleChars(aOutStream, char(Root), HTree^[Root].hnCount)
  930.     {otherwise, using the Huffman tree, decompress the characters in
  931.      the input stream; note that the number of chars to decompress
  932.      is the count at the root of the Huffman tree}
  933.     else begin
  934.       HTree^[Root].hnCount := Size;
  935.       DoHuffmanDecompression(InputBitStream, aOutStream, HTree, Root);
  936.     end;
  937.   finally
  938.     if (HTree <> nil) then
  939.       Dispose(HTree);
  940.     InputBitStream.Free;
  941.   end;
  942. end;
  943. {====================================================================}
  944.  
  945. end.
  946.